<?php
class  Curl extends BaseClass
{
	public $Output = '';#curl output buffer
	public $Options = array();
	public $CustomOptions = array();
	private $CurlHandler;
	private $IsDebugMode = false;
	public $Headers = array();
	public $MultiHeaders = array();
	public $Connections = array();
	public $RequestInfo = array();

	function __construct($url = '')
	{
		parent::__construct();

		$this->Options['CURLOPT_USERAGENT'] = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6';
		$this->Options['CURLOPT_URL'] = $url;
		$this->Options['CURLOPT_RETURNTRANSFER'] = 1;
		$this->Options['CURLOPT_TIMEOUT'] = 30;
		$this->Options['CURLOPT_SSL_VERIFYHOST'] = 0;
		$this->Options['CURLOPT_SSL_VERIFYPEER'] = 0;

		//$this->Options['CURLOPT_HEADER'] = 1;
		//$this->Options['CURLOPT_HEADERFUNCTION'] = array(&$this, 'ReadHeader');
		
		$this->cache = new DataCache();
		
		$this->IsDebugMode = $this->cache->Get('curl_debug_mode');
	}

	//=======================================================================
		
	public function GetStats()
	{
		if (!$this->IsDebugMode) die('Debug mode is disabled. To enable it run this script with "?enable=1", to disable - with "?disable=1"');
		
		require_once 'lib/php/functions/time_ago.php';
		
		$ret = 'Debug started '. time_ago($this->IsDebugMode)." ago\n";
		$ret .= "\n--------------------- single requests stats ------------------------\n";
		$ret .= 'Total single GET requests - '.($cnt = intval($this->cache->Get('curl_debug_get_cnt'))).'; their total recieved traffic - '.number_format($total_traffic = intval($this->cache->Get('curl_debug_get_traffic')))."\n";
		$ret .= round($cnt / (time() - $this->IsDebugMode), 5).' req/sec; '.round($total_traffic / (time() - $this->IsDebugMode)).' bytes/sec'."\n";
		$ret .= 'Response codes: '.$this->GetStatsbyCodes()."\n";
		
		$ret .= "\n--------------------- multi requests stats ------------------------\n";
		$ret .= 'Mutli GET requests - '.($cnt = intval($this->cache->Get('curl_debug_get_multi_cnt'))).'/'.intval($this->cache->Get('curl_debug_get_multi_single_cnt')).'; their total recieved traffic - '.number_format($total_traffic = intval($this->cache->Get('curl_debug_get_multi_traffic')))."\n";
		$ret .= round($cnt / (time() - $this->IsDebugMode), 5).' req/sec; '.round($total_traffic / (time() - $this->IsDebugMode)).' bytes/sec'."\n";
		$ret .= 'Response codes: '.$this->GetStatsbyCodes('curl_debug_response_code_multi_')."\n";
		
		$ret .= "\n--------------------- post stats ------------------------\n";
		$ret .= 'Total POST requests - '.($cnt = intval($this->cache->Get('curl_debug_post_cnt'))).'; their total recieved traffic - '.number_format($total_traffic = intval($this->cache->Get('curl_debug_post_traffic')))."\n";
		$ret .= round($cnt / (time() - $this->IsDebugMode), 5).' req/sec; '.round($total_traffic / (time() - $this->IsDebugMode)).' bytes/sec'."\n";
		$ret .= 'Response codes: '.$this->GetStatsbyCodes('curl_debug_response_post_code_')."\n";

		return $ret;
	}
	
	//=======================================================================
	
	private function GetStatsbyCodes($key_prefix = 'curl_debug_response_code_')
	{
		$ret = '200 => '.intval($this->cache->Get($key_prefix.'200')).'; ';
		$ret .= '404 => '.intval($this->cache->Get($key_prefix.'404')).'; ';
		$ret .= '500 => '.intval($this->cache->Get($key_prefix.'500')).'; ';
		$ret .= '302 => '.intval($this->cache->Get($key_prefix.'302')).'; ';
		$ret .= '301 => '.intval($this->cache->Get($key_prefix.'301')).'; ';
		
		return $ret;
	}
	
	//=======================================================================
	
	public function SetDebugMode($value)
	{
		if ($value)
		{
			$this->cache->Set('curl_debug_mode', time(), 100000);
		}
		else
		{
			$this->cache->Delete('curl_debug_mode');
			
			$this->cache->Delete('curl_debug_get_cnt');
			$this->cache->Delete('curl_debug_get_traffic');
			
			$this->cache->Delete('curl_debug_post_cnt');
			$this->cache->Delete('curl_debug_post_traffic');
			
			$this->cache->Delete('curl_debug_get_multi_cnt');
			$this->cache->Delete('curl_debug_get_multi_single_cnt');
			$this->cache->Delete('curl_debug_get_multi_traffic');
			
			$this->cache->Delete('curl_debug_response_post_code_200');
			$this->cache->Delete('curl_debug_response_post_code_302');
			$this->cache->Delete('curl_debug_response_post_code_301');
			$this->cache->Delete('curl_debug_response_post_code_404');
			$this->cache->Delete('curl_debug_response_post_code_500');
			
			$this->cache->Delete('curl_debug_response_code_200');
			$this->cache->Delete('curl_debug_response_code_302');
			$this->cache->Delete('curl_debug_response_code_301');
			$this->cache->Delete('curl_debug_response_code_404');
			$this->cache->Delete('curl_debug_response_code_500');
			
			$this->cache->Delete('curl_debug_response_code_multi_200');
			$this->cache->Delete('curl_debug_response_code_multi_302');
			$this->cache->Delete('curl_debug_response_code_multi_301');
			$this->cache->Delete('curl_debug_response_code_multi_404');
			$this->cache->Delete('curl_debug_response_code_multi_500');

		}
	}
	
	//=======================================================================
	
	public function DbDebug($url, $type, $size, $code)
	{
		if (empty($this->Dbh))
		{
			$this->Dbh = Mysql::GetInstance();
		}
		
		$url_parts = parse_url($url);
		$ins = array('host' => $url_parts['host'], 'full_url' => $url, 'type' => $type, 'size' => $size, 'date' => 'NOW()', 'code' => $code);

		$res = $this->Dbh->Insert('curl_debug', $ins);
	}
	
	//=======================================================================

	function SendPostRequest($data = array(), $url = '')
	{
		$this->CurlHandler = curl_init();

		if (!empty($url))
		{
			$this->Options['CURLOPT_URL'] = $url;
		}
		$this->Options['CURLOPT_POST'] = 1;
		$this->Options['CURLOPT_POSTFIELDS'] = $this->PrepareRequest($data);

		$this->Process();

		unset($this->Options['CURLOPT_POST']);
		unset($this->Options['CURLOPT_POSTFIELDS']);

		if ($this->IsDebugMode)
		{
			$this->cache->Increment('curl_debug_post_cnt', 1, 100000);
			$this->cache->Increment('curl_debug_response_post_code_'.$this->RequestInfo['http_code'], 1, 100000);
			$size = strlen($this->Output);
			if (empty($size) && isset($this->RequestInfo['hidden_output']))
			{
				$size = strlen($this->RequestInfo['hidden_output']);
			}
			
			$this->cache->Increment('curl_debug_post_traffic', $size, 100000);
			
			//insert into curl debug table
			$this->DbDebug($this->Options['CURLOPT_URL'], 'post', $size, $this->RequestInfo['http_code']);
		}

		return $this->Output;
	}

	//=======================================================================

	function ReadHeader(&$ch, $header)
	{
		if (!empty($header))
		{
			$parts = explode(': ', $header);
			if (count($parts) == 2)
			{
				$this->Headers[strtolower($parts[0])] = $parts[1];
			}
			/*
			elseif (empty($this->Headers))
			{
				$this->Headers[0] = $header;
			}
			*/
		}
		return strlen($header);
	}

	//=======================================================================

	function SendHeadRequest($data = array(), $url = '')
	{
		$this->CurlHandler = curl_init();
		if (!empty($url))
		{
			$this->Options['CURLOPT_URL'] = $url;
		}
		$url_query = $this->PrepareRequest($data);
		if (!empty($url_query))
		{
			$this->Options['CURLOPT_URL'] .= '?'.$url_query;
		}
		$this->Options['CURLOPT_NOBODY'] = 1;
		$this->Options['CURLOPT_HEADER'] = 1;
		$this->Options['CURLOPT_HEADERFUNCTION'] = array(&$this, 'ReadHeader');

		$this->Process();

		unset($this->Options['CURLOPT_NOBODY']);
		unset($this->Options['CURLOPT_HEADER']);
		unset($this->Options['CURLOPT_HEADERFUNCTION']);

		return $this->Output;
	}

	//=======================================================================

	function sendGetRequest($data = array(), $url = '')
	{
		$this->CurlHandler = curl_init();
		if (!empty($url))
		{
			$this->Options['CURLOPT_URL'] = $url;
		}
		$url_query = $this->PrepareRequest($data);
		if (!empty($url_query))
		{
			$this->Options['CURLOPT_URL'] .= '?'.$url_query;
		}

		$this->Process();
		
		if ($this->IsDebugMode)
		{
			$this->cache->Increment('curl_debug_get_cnt', 1, 100000);
			$this->cache->Increment('curl_debug_response_code_'.$this->RequestInfo['http_code'], 1, 100000);
			$size = strlen($this->Output);
			if (empty($size) && isset($this->RequestInfo['hidden_output']))
			{
				$size = strlen($this->RequestInfo['hidden_output']);
			}
			
			$this->cache->Increment('curl_debug_get_traffic', $size, 100000);
			
			$this->DbDebug($this->Options['CURLOPT_URL'], 'get', $size, $this->RequestInfo['http_code']);
		}
		
		return $this->Output;
	}

	//=======================================================================

	function Process() {

		if(!is_resource($this->CurlHandler)) {
			$this->setTextError('Curl has not be initialized');
			return false;
		}

		#setting up curl options
		$options = array_merge($this->Options, $this->CustomOptions);
		foreach($options as $key => $values)
		{
			curl_setopt($this->CurlHandler, constant($key), $values);
		}

		#process request
		$output =  curl_exec($this->CurlHandler);
		$this->CustomOptions = array();
		$this->RequestInfo = curl_getinfo($this->CurlHandler);

		if ($this->RequestInfo['http_code'] != 200)
		{
			$this->RequestInfo['hidden_output'] = $output;
			$output = '';
		}

		if(empty($output))
		{
			$this->setTextError('Null size reply '.curl_error($this->CurlHandler));
			curl_close ($this->CurlHandler);
			return false;
		}

		if( curl_errno($this->CurlHandler) == 0)
		{
			curl_close ($this->CurlHandler);
			return $this->Output = $output;
		}
		else
		{
			$this->setTextError('Error, while process request: '.curl_error($this->CurlHandler));
			curl_close($this->CurlHandler);
			return false;
		}
	}

	//==============================================================================

	function PrepareRequest($data)
	{
		if (empty($data) || !is_array($data)) return '';
		$parts = array();
		foreach ($data as $field => $value)
		{
			$parts[] = $field.'='.$value;
		}

		return implode('&', $parts);
	}

	//==============================================================================

	function SendMultiRequest($urls)
	{
		if (empty($urls))
		{
			$this->setTextError('Curl: Multi init error');
			return;
		}
		if (isset($this->Options['CURLOPT_URL']))
		{
			unset($this->Options['CURLOPT_URL']);
		}
		$this->CurlMultiHandler = curl_multi_init();
		foreach ($urls as $key => $params)
		{
			if (!is_numeric($key))
			{
				$url = $key;
			}
			else
			{
				$url = !empty($params['url']) ? $params['url'] : '';
				if (empty($url))
				{
					$this->setTextError('Curl: There is no url for request');
					return;
				}
			}
			$this->Connections[$key] = curl_init($url);

			//set url
			$this->Options['CURLOPT_URL'] = $url;
			if (!empty($params['GET']))
			{
				$url_query = $this->PrepareRequest($params['GET']);
				if (!empty($url_query))
				{
					$this->Options['CURLOPT_URL'] .= '?'.$url_query;
				}
			}

			//set common options
			$options = array_merge($this->Options, $this->CustomOptions);
			foreach ($options as $code => $value)
			{
				curl_setopt($this->Connections[$key], constant($code), $value);
			}

			//process redirects
			curl_setopt($this->Connections[$key], CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($this->Connections[$key], CURLOPT_MAXREDIRS, 1);

			//set post method and data if need
			if (!empty($params['POST']))
			{
				curl_setopt($this->Connections[$key], CURLOPT_POST, 1);
				curl_setopt($this->Connections[$key], CURLOPT_POSTFIELDS, $this->PrepareRequest($params['POST']));
			}
			if (!empty($params['COOKIEFILE']))
				curl_setopt($this->Connections[$key], CURLOPT_COOKIEFILE, $params['COOKIEFILE']);
			if (!empty($params['COOKIEJAR']))
				curl_setopt($this->Connections[$key], CURLOPT_COOKIEJAR, $params['COOKIEJAR']);

			curl_multi_add_handle ($this->CurlMultiHandler, $this->Connections[$key]);
		}
		// start performing the request
		do{
			$mrc = curl_multi_exec($this->CurlMultiHandler, $active);
		}while ($mrc == CURLM_CALL_MULTI_PERFORM);

		while ($active and $mrc == CURLM_OK)
		{
			// wait for network
			if (curl_multi_select($this->CurlMultiHandler) != -1)
			{
				// pull in any new data, or at least handle timeouts
				do{
					$mrc = curl_multi_exec($this->CurlMultiHandler, $active);
				}while ($mrc == CURLM_CALL_MULTI_PERFORM);
			}
		}
		$res = array();
		// retrieve data
		
		$code_responses = array();
		$total_size = 0;
		
		foreach ($urls as $key => $params)
		{
			if (($err = curl_error($this->Connections[$key])) == '')
			{
				//set post method and data if need
				$res[$key] = curl_multi_getcontent($this->Connections[$key]);
			}
			else
			{
				$this->setTextError("Curl error on handle $key: $err\n");
			}
			
			$this->MultiHeaders[$key] = curl_getinfo($this->Connections[$key]);
			
			curl_multi_remove_handle($this->CurlMultiHandler, $this->Connections[$key]);
			curl_close($this->Connections[$key]);
			
			if ($this->IsDebugMode)
			{
				$c = $this->MultiHeaders[$key]['http_code'];
				if (!isset($code_responses[$c]))
				{
					$code_responses[$c] = 1;
				}
				else
				{
					$code_responses[$c]++;
				}
				
				$total_size += $this->MultiHeaders[$key]['size_download'];
				
				$this->DbDebug($this->MultiHeaders[$key]['url'], 'multi', $this->MultiHeaders[$key]['size_download'], $this->MultiHeaders[$key]['http_code']);
			}
			
		}

		curl_multi_close($this->CurlMultiHandler);

		if ($this->IsDebugMode)
		{
			$this->cache->Increment('curl_debug_get_multi_cnt', 1, 100000);
			$this->cache->Increment('curl_debug_get_multi_single_cnt', count($this->Connections), 100000);
			
			foreach ($code_responses as $code => $count)
			{
				$this->cache->Increment('curl_debug_response_code_multi_'.$code, $count, 100000);
			}
						
			$this->cache->Increment('curl_debug_get_multi_traffic', $total_size, 100000);
		}

		return $res;
	}

	//==============================================================================

	function SetOption($option_name, $value)
	{
		$this->CustomOptions[$option_name] = $value;
	}

	//==============================================================================

}
?>
